﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmartMathLibrary
{
    /// <summary>
    /// This class provides the approximation of the third derivative of a function f(x) by using a small limiting 
    /// value.
    /// </summary>
    [Serializable]
    public class RealThirdDerivativeApproximator
    {
        /// <summary>
        /// The source function for the to approximate third derivative.
        /// </summary>
        private IRealFunction sourceFunction;

        /// <summary>
        /// Initializes a new instance of the <see cref="RealThirdDerivativeApproximator"/> class.
        /// </summary>
        /// <param name="sourceFunction">The source function for the to approximate third derivative.</param>
        public RealThirdDerivativeApproximator(IRealFunction sourceFunction)
        {
            this.sourceFunction = sourceFunction;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="RealThirdDerivativeApproximator"/> class.
        /// </summary>
        /// <param name="sourceFunction">The source function for the to approximate third derivative.</param>
        public RealThirdDerivativeApproximator(Polynomial sourceFunction)
        {
            this.sourceFunction = sourceFunction;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="RealThirdDerivativeApproximator"/> class.
        /// </summary>
        /// <param name="sourceFunction">The source function for the to approximate third derivative.</param>
        public RealThirdDerivativeApproximator(SimplePolynomial sourceFunction)
        {
            this.sourceFunction = sourceFunction;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="RealThirdDerivativeApproximator"/> class.
        /// </summary>
        /// <param name="sourceFunction">The source function for the to approximate third derivative.</param>
        public RealThirdDerivativeApproximator(SimpleRationalPolynomial sourceFunction)
        {
            if (sourceFunction == (SimpleRationalPolynomial) null)
            {
                throw new ArgumentNullException("sourceFunction");
            }

            this.sourceFunction = sourceFunction;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="RealThirdDerivativeApproximator"/> class.
        /// </summary>
        /// <param name="sourceFunction">The source function for the to approximate third derivative.</param>
        public RealThirdDerivativeApproximator(HardRealFunction sourceFunction)
        {
            this.sourceFunction = sourceFunction;
        }

        /// <summary>
        /// Gets or sets the source function for the to approximate derivative.
        /// </summary>
        /// <value>The source function for the to approximate derivative.</value>
        public IRealFunction SourceFunction
        {
            get { return sourceFunction; }
            set { sourceFunction = value; }
        }

        /// <summary>
        /// Approximates the specified real function derivative by using a small limiting value 1e-5.
        /// </summary>
        /// <param name="x">The x at which the function derivative should be calculate.</param>
        /// <returns>The specified slope at the position x.</returns>
        public double LimitingValueApproximation(double x)
        {
            return this.LimitingValueApproximation(x, 1e-5);
        }

        /// <summary>
        /// Approximates the specified real function derivative by using a small limiting value.
        /// </summary>
        /// <param name="x">The x at which the function derivative should be calculate.</param>
        /// <param name="epsilon">The epsilon of the small limiting value.</param>
        /// <returns>The specified slope at the position x.</returns>
        public double LimitingValueApproximation(double x, double epsilon)
        {
            return (this.sourceFunction.SolveAt(x + 2 * epsilon) - 3 * this.sourceFunction.SolveAt(x + epsilon) +
                    3 * this.sourceFunction.SolveAt(x) - this.sourceFunction.SolveAt(x - epsilon)) /
                   Math.Pow(epsilon, 3);
        }
    }
}